#include "MemoryStream.h"


byte* MemoryStream::AllocBlock()
{
	return (byte*)HeapAlloc( GetProcessHeap() , HEAP_ZERO_MEMORY , m_blockSize );
}

int32 MemoryStream::Uninitalize()
{
	typedef Blocks::iterator iterator;

	iterator first = m_block.begin();
	iterator last  = m_block.end();

	int32 code = R_SUCCEED;

	for( ; first != last ; ++first )
	{
		if( ! HeapFree( GetProcessHeap() , 0 , (*first) ) )
			code = E_MEMROY_UNINITFREE;
	}

	return code;
}

int32 MemoryStream::AllocBlocks( int64 allocSize )
{
	uint64 blockCount = ( allocSize / m_blockSize ) + 1;

	uint32 code = R_SUCCEED;

	for( uint64 ix = 0 ; ix< blockCount ; ++ix )
	{
		byte* block = AllocBlock();
		if( block == 0 )
		{
			code = E_MEMROY_ALLOCBLOCK;
			break;
		}

		m_block.push_back( block );

		m_length += m_blockSize;
	}

	return R_SUCCEED;
}

int32 MemoryStream::GetCurrentBlock( byte*& block , uint32& blockSize )
{
	int64  blockCurrent  = m_current / m_blockSize;
	int64  blockOffset   = m_current - ( m_blockSize * blockCurrent );
	uint64 sizeBlock     = 0;
#pragma warning( push )
#pragma warning( disable:4244)
	byte*  blockMemory   = m_block[blockCurrent];
#pragma warning( pop )
	block       = blockMemory + blockOffset;
	sizeBlock   = block       - blockMemory;

	blockSize   = static_cast<uint32>( m_blockSize - sizeBlock );

	return R_SUCCEED;
}

int32 MemoryStream::Initalize( uint64 allocSize  , uint32 blockSize  )
{
	if( ! m_block.empty() )
		return E_MEMORY_INITALIZED;

	m_blockSize = blockSize;

	if( allocSize == 0 )
		return R_SUCCEED;

	uint32 code = AllocBlocks( allocSize );

	if( code != R_SUCCEED )
	{
		int32 codeUninit = Uninitalize();
		if( codeUninit != R_SUCCEED )
			return codeUninit;
	}

	return code;
}

int32 MemoryStream::Seek( uint64 position , SEEK_POSITION flag  ) 
{
	uint64 newPosition = 0;

	if( flag == CURRENT )
		newPosition = m_current + position;
	else if(flag == END )
		newPosition = m_length  - position;
	else
		newPosition = position;
	if(newPosition > m_length )
		return E_MEMORY_INVALIDPOS;
	m_current = position;
	return R_SUCCEED;
}

int32 MemoryStream::GetPosition( uint64& position ) 
{
	position = m_current;

	return R_SUCCEED;
}

int32 MemoryStream::EndOfStream() 
{
	if( m_current == m_length )
		return R_SUCCEED;
	return R_NOT_ENDOFSTREAM;
}

int32 MemoryStream::Write( byte* block , uint32 blockSize , uint32& fileWrite ) 
{
	int32 code      = R_SUCCEED;
	int64 freeSize  = GetFreeSize();
	int64 writeSize = static_cast<int64>(blockSize);
	if( freeSize < writeSize )
	{
		int64 allocSize = writeSize - freeSize;
		code = AllocBlocks( allocSize );

		if(code != R_SUCCEED)
			return code;
	}

	uint32 sizeBlock   = 0;
	byte*  blockMemory = 0;

	uint32 copySize    = 0;

	while( copySize < blockSize )
	{
		GetCurrentBlock( blockMemory , sizeBlock );

		uint32 writeSize   = blockSize - copySize;
		uint32 blockWrite  = (writeSize > sizeBlock)?sizeBlock:writeSize;
		byte*  writeMemory = block + copySize;
		memcpy_s( blockMemory , blockWrite , writeMemory , blockWrite );

		copySize  += blockWrite;
		m_current += blockWrite;
		m_end     += blockWrite;
		fileWrite += blockWrite;
	}

	return R_SUCCEED;
}

int32 MemoryStream::Read( byte* block , uint32 blockSize , uint32& fileRead ) 
{
	if( m_block.empty() || EndOfStream() != R_SUCCEED )
		return E_MEMORY_EMPTY;

	uint64 dataSize = GetDataSize();
	uint64 readSize = ( blockSize > dataSize )?dataSize:blockSize;

	uint32 copySize = 0;

	while(copySize < readSize )
	{
		byte*  blockMemory = 0;
		uint32 sizeBlock   = 0;
		GetCurrentBlock( blockMemory , sizeBlock );

		uint32 sizeRead  = blockSize - copySize;
		uint32 blockRead = (sizeRead > sizeBlock)?sizeBlock:sizeRead;
		byte*  readMemory= block + copySize;
		memcpy_s( readMemory , blockRead , blockMemory , blockRead );

		fileRead += blockRead;
		copySize += blockRead;
		m_current+= blockRead;
	}

	return R_SUCCEED;
}

int32 MemoryStream::Flush() 
{
	return R_SUCCEED;
}

int32 MemoryStream::GetSize( uint64& size ) 
{
	size = m_length;
	return R_SUCCEED;
}

int32 MemoryStream::Close() 
{
	return Uninitalize();
}

int32 MemoryStream::CanWrite() 
{
	return R_SUCCEED;
}

int32 MemoryStream::CanSeek() 
{
	return R_SUCCEED;
}

int32 MemoryStream::HaveEOS()
{
	return R_SUCCEED;
}

int32 MemoryStream::CanRead() 
{
	return R_SUCCEED;
}

MemoryStream::~MemoryStream()
{
	if( ! m_block.empty() )
		Uninitalize();
}

MemoryStream::MemoryStream() 
: m_current(0) , m_length(0) , m_blockSize( 512*KB ) , m_end(0)
{

}